<?php
!defined('IN_MUDDER') && exit('Access Denied');

/**
 * URL重写模式
 */
define('MF_URL_REWRITE_NORMAL', 		'normal');
define('MF_URL_REWRITE_HTML', 		'html');
define('MF_URL_REWRITE_PATH', 		'pathinfo');
/**
 * 二级域名方案
 */
define('MF_URL_SLDOMAIN_NONE',			'');
define('MF_URL_SLDOMAIN_CITYSITE', 		'citysite');
define('MF_URL_SLDOMAIN_MODULE', 		'module');
/**
 * 分站URL展示模式
 */
define('MF_URL_CITY_NORMAL', 		0);
define('MF_URL_CITY_SUBDOMAIN',		1);
define('MF_URL_CITY_PATHNAME', 		2);
/**
 * URL替换行为
 */
define('MF_URL_PREG_PARSE',		'P');
define('MF_URL_PREG_CREATE',	'C');

/**
 * URL管理类
 */
class ms_url 
{
	/**
	 * url地址是否存在（能指向相应的模块页面）
	 * @var boolean
	 */
	private $url_not_found = false;
	/**
	 * 二级域名方案
	 * @var string
	 */
	private $sldomain_mod = '';
	/**
	 * 当前页面URL所使用的url重写模式，一般和下方一致，不一致的话一般是中途修改过url重写模式
	 * 而之前的URL已经被搜索引擎收录，从搜索引擎进入是和当前配置的不一致造成页面无法访问的现象
	 * @var string
	 */
	private $real_rewrite_mod = '';
	/**
	 * 当前来路URL是否隐藏了index.php (并非系统设置，而是当前访问的URL上进行的判断)
	 * @var string
	 */
	private $real_hide_index = true;
	/**
	 * 系统配置设置的url重写模式
	 * @var string
	 */
	private $rewrite_mod = 'normal';
	/**
	 * 系统配置设置的城市分站url模式
	 * @var integer
	 */
	private $subsite_mod = 0;
	/**
	 * 来路URL重写模式和系统设置的不一致时是否进行跳转
	 * @var boolean
	 */
	private $rewrite_location = false;
	/**
	 * 系统配置设置的是否隐藏index.php
	 * @var integer
	 */
	private $hide_index = 0;
	/**
	 * 中文兼容
	 * @var boolean
	 */
	private $chinses_compatible = false;
	/**
	 * url地址中获取的城市域名前缀（目录名）
	 * @var string
	 */
	private $subdomain = '';
	/**
	 * url地址解析后的url表达式
	 * @var string
	 */
	private $url_exp = '';
	/**
	 * url解析后的数组
	 * @var array
	 */
	private $params = array();
	/**
	 * url参数解析后进行规则配对成键名+键值数组
	 * @var array
	 */
	private $get = array();
	/**
	 * 当前URL是index.php/形式模拟URL重写
	 * @var boolean
	 */
	private $use_sim = false;
	/**
	 * 当前URL是否使用了URL重写
	 * @var boolean
	 */
	private $use_rewrite = false;
	/**
	 * 当前URL是否使用了分站URL
	 * @var boolean
	 */
	private $use_subsite_url = false;

	/**
	 * 构造函数，加载配置，解析当前页面URL
	 */
	public function __construct() 
	{
		$this->_init_config();
		$this->_pares_url();
	}

	/**
	 * 提供初始url参数，根据索引号取得
	 * @param  int $index 数组下标
	 * @return [type]
	 */
	public function param($index = null)
	{
		if($index==null) return $this->params;
		return $this->params[$index];
	}

	/**
	 * 判断url重写模式是否和系统设置的一致
	 * @return bool
	 */
	public function rewirte_mod_compare()
	{
		if(!$this->real_rewrite_mod) return TRUE;
		return $this->rewrite_mod == $this->real_rewrite_mod && $this->hide_index == $this->real_hide_index;
	}

	/**
	 * 获取当前页面解析后的URL表达式
	 * @return string
	 */
	public function get_url_exp()
	{
		return $this->url_exp;
	}

	/**
	 * 访问页面不存在
	 * @return boolean
	 */
	public function is_404()
	{
		return $this->url_not_found;
	}

	/**
	 * 访问页面是否使用了rewrite功能,normal不属于rewrite
	 * @return boolean
	 */
	public function is_rewrite()
	{
		return $this->use_rewrite;
	}

	/**
	 * 当前域名是否和后台设置属于同一个一级域名
	 * @return boolean [description]
	 */
	public function is_diff_domain()
	{
		return $this->url?$this->url->diff_domain:false;
	}

	/**
	 * 检测当前URL是否是全局URL（URL没有加入识别分站标识的参数或者子域名）
	 * @return boolean [description]
	 */
	public function is_global_url()
	{
		$r = $this->get_subsite();
		return empty($r);
	}

	/**
	 * 当前URL是否存在无法识别的子域名
	 * @return boolean [description]
	 */
	public function is_unknow_subdomain()
	{
		$r = $this->get_unknow_subdomain();
		return !empty($r);
	}

	/**
	 * 判断当前URL是否加入了分站URL（使用了子域名或者分站目录）
	 * @return boolean [description]
	 */
	public function get_subsite()
	{
		return $this->url?$this->url->subsite:false;
	}

	/**
	 * 获取子域名前缀
	 * @return string
	 */
	public function get_subdomainn()
	{
		return $this->url?$this->url->subdomain:false;
	}

	/**
	 * 子域名是否未未知（无法识别）的，如果是，则返回子域名
	 * @return string
	 */
	public function get_unknow_subdomain()
	{
		return $this->url?$this->url->unknow_subdomain:false;
	}

	/**
	 * 获取当前系统URL重写模式
	 * @return string
	 */
	public function get_rewrite_mod()
	{
		return $this->rewrite_mod;
	}

	/**
	 * 获取当前系统城市分站URL形式
	 * @return int
	 */
	public function get_subsite_mod()
	{
		return $this->subsite_mod;
	}

	/**
	 * url表达式生成url地址, rewrite_mod类型
	 * @param  string  $url_exp     	URL表达式
	 * @param  boolean $full_url    	完整路径
	 * @param  string  $rewrite_mod 	URL重写类型
	 * @param  string  $subsite_mod 		分站URL显示模式
	 * @param  boolean 	$include_subsite_url	URL是否强制加入分站
	 * @return string
	 */
	public function create($url_exp, $full_url = false, $rewrite_mod = null, $subsite_mod = null, $include_subsite_url = FALSE)
	{
		static $cache = array();

		is_null($rewrite_mod) && $rewrite_mod = $this->rewrite_mod;
		is_null($subsite_mod) && $subsite_mod = $this->subsite_mod;
		is_null($sldomain_mod) && $sldomain_mod = $this->sldomain_mod;

		$url_exp = trim($url_exp);

		//非url表达式，为完整url地址，就直接返回地址
		if(strtolower(substr($url_exp, 0, 4)) == 'http') {
			return $url_exp;
		}

		//从缓存中读取，减少生成URL所损耗的时间
		if($cache_info = $cache[$url_exp]) {
			list($url_address, $p2, $p3, $p4, $p5, $p6) = $cache_info;
			if($p2 === $full_url && $p3 === $rewrite_mod && $p4 === $sldomain_mod && 
				$p5 === $sldomain_mod && $p6 === $include_subsite_url) 
				return $url_address;
		}

		//url表达式初步解析, 然后交给url改写类生成,最后返回真实的url地址
		$url_address = $this->_create($url_exp, $full_url, $rewrite_mod, $sldomain_mod, $subsite_mod, $include_subsite_url);
		//讲解析的地址直接存入缓存，方便下次读取(存储时也存入是否完整路径以及URL改写模式)
		$cache[$url_exp] = array($url_address, $full_url, $rewrite_mod, $sldomain_mod, $subsite_mod, $include_subsite_url);

		return $url_address;
	}

	/**
	 * 判断当前模块行为生成的URL是否为全局URL（没有分站二级域名，分站目录形式）
	 * @param  string $module 模块标识
	 * @param  string $act    模块内行为
	 * @return boolean
	 */
	public function use_global_url($module, $act)
	{
		return $this->_domain_without($module, $act);
	}

	/**
	 * url相关配置初始化
	 * @return [type] [description]
	 */
	private function _init_config()
	{
		//二级域名方案
		$this->sldomain_mod = S('sldomain_mod')?:'';

		//分站URL显示模式
		$this->subsite_mod = S('city_sldomain');
		if($this->subsite_mod == MF_URL_CITY_SUBDOMAIN && $this->sldomain_mod != MF_URL_SLDOMAIN_CITYSITE) {
			$this->subsite_mod = MF_URL_CITY_NORMAL;
		}
		//分站使用二级域名
		if($this->sldomain_mod == MF_URL_SLDOMAIN_CITYSITE) {
			$this->subsite_mod == MF_URL_CITY_SUBDOMAIN;
		}

		//URL重写模式
		$this->rewrite_mod = S('rewrite') ? S('rewrite_mod') : MF_URL_REWRITE_NORMAL;

		//入口文件模拟Rewrite
		$this->hide_index = S('rewrite_hide_index') ? true : false;

		//来路不一致跳转性
		$this->rewrite_location = S('rewrite_location') ? true : false;
		$this->chinses_compatible = S('rewritecompatible') ? true : false;
		if(!$this->rewrite_mod) $this->rewrite_mod = MF_URL_REWRITE_NORMAL;

		//伪静态形式URL重写不能使用目录形式分站
		if(in_array($this->rewrite_mod, array(MF_URL_REWRITE_HTML, MF_URL_REWRITE_NORMAL)) 
			&& MF_URL_CITY_PATHNAME == $this->subsite_mod) {
			$this->subsite_mod = MF_URL_CITY_NORMAL;
			S('city_sldomain', MF_URL_CITY_NORMAL); //改写系统配置
		}

		//尚未判断当前来路是否隐藏index前，保持和系统设置一致
		$this->real_hide_index = $this->hide_index;
	}

	/**
	 * url表达式初步解析
	 * @param  string 	$url_exp		URL表达式
	 * @param  boolean 	$full_url		是否显示完整路径
	 * @param  string 	$rewrite_mod 	URL模式类型
	 * @param  string 	$sldomain_mod	二级域名方案
	 * @param  string 	$subsite_mod	分站属性模式
	 * @param  boolean 	$include_subsite_url	URL是否强制加入分站
	 * @return string
	 */
	private function _create($url_exp, $full_url, $rewrite_mod, $sldomain_mod, $subsite_mod, $include_subsite_url = FALSE)
	{
		static $domains = array();
		static $city = null;
		static $domain_without = array();
		static $urls = null;

		$domain = $city_id = null;
		$url_exp = trim(trim($url_exp), '/');
		$info = explode("/", $url_exp);

		//参数数组
		$url_exp_arr = array();

		//url表达式包含指定的城市ID
		if(preg_match("/^city:([0-9]+)$/i", $info[0], $matches)) {
			$city_id = (int) $matches[1];
			$url_exp2 = substr($url_exp, strlen($matches[0]) + 1);
			$info = $url_exp2 ? explode('/', substr($url_exp, strlen($matches[0]) + 1)) : '';
			if($city_id > 0) {
				$domain = $domains[$city_id];
				if(!$domain) {
					$domain = display('modoer:area',"aid/$city_id/keyname/domain");
					$domains[$city_id] = $domain;
				}
				$url_exp_arr['city'] = $domain;
			} else {
				$domain = '{GLOBAL}';
			}
		}
		if(empty($info)) {
			$url_exp = "index";
			$url_exp_arr['module'] = $module = "modoer";
			$url_exp_arr['act'] = $act = "";
		} else {
			strtolower($info[0]) == 'modoer' && $info[0] = 'index';
			$url_exp_arr['module'] = $module = $info[0];
			$url_exp_arr['act'] = $act = $info[1];
			if(count($info) > 2) {
				//排除是否有空值现象，如果是空值，则从表达式中删除对应的参数（键和值）
				$newinfo = array(0 => $module, 1 => $act);
				$params = array();
				for ($i = 2; $i < count($info); $i = $i + 2) { 
					if($info[$i + 1] == '') continue;
					$newinfo[$i] = $info[$i];
					$newinfo[$i + 1] = $info[$i + 1];
					$params[$info[$i]] = $info[$i + 1];
				}
				$url_exp = implode('/', $newinfo);
				$url_exp_arr['params'] = $params;
			} else {
				$url_exp = implode('/', $info);
				if(!$act) {
					$act = 'index';
					$url_exp = $url_exp.'/index';
				}
			}
		}
		
		//首页
		if(in_array(strtolower($url_exp), array('index', 'index/index'))) {
			$url_exp = '';
		}
		if(!$city) $city = G('city'); //获取当前城市信息
		if($domain == '{GLOBAL}') {
			$domain = '';
			$url_exp_arr['city'] = '';
		} elseif (!$domain && $city) {
			//判断模块或行为是否需要城市分站子域名或者城市目录
			$domain = $city['domain'];
			$url_exp_arr['city'] = $domain;
		}

		//当前模块行为产生的URL是否是全局URL（不包含子域名或城市目录），如果是全局URL，则传入domain参数
		$dw_key = "{$module}_{$act}";
		if(!isset($domain_without[$dw_key])) {
			$domain_without[$dw_key] = $this->_domain_without($module, $act); //检测是否使用全局URL
		}

		//准备生成URL
		if(!$urls[$rewrite_mod]) {
			//实例化URL生成类
			$urls[$rewrite_mod] = $this->_factory($rewrite_mod);
		}
		$url = $urls[$rewrite_mod];
		if(!$url) return;

		if($sldomain_mod==MF_URL_SLDOMAIN_MODULE) {
			$sldomain_modues = S('sldomain_modues')?unserialize(S('sldomain_modues')):array();
			if(!in_array($module,$sldomain_modues)) $sldomain_mod=MF_URL_SLDOMAIN_NONE;
		}

		$url->set_hide_index($this->hide_index); 	//是否显示入口文件
		$url->set_sldomain_mod($sldomain_mod);		//分站URL显示模式
		$url->set_subsite_mod($subsite_mod);		//分站URL显示模式
		//$address = $url->create($url_exp, $domain);	//开始生成

		$address = $url->create2($url_exp_arr, $include_subsite_url||!$domain_without[$dw_key], $full_url);

		return $address;


		//不需要分站属性
		if(/*!$city_id && */$domain && $url_exp && $domain_without[$dw_key]) {
			$domain = '';
			//使用子域名做分站，则必须要完整路径
			if($subsite_mod == MF_URL_CITY_SUBDOMAIN || $sldomain_mod == MF_URL_SLDOMAIN_MODULE) {
				$full_url = true;
			}
		}

		if(!$urls[$rewrite_mod]) {
			$urls[$rewrite_mod] = $this->_factory($rewrite_mod);
		}
		$url = $urls[$rewrite_mod];
		if(!$url) return;
		$url->set_hide_index($this->hide_index); 	//是否显示入口文件
		$url->set_sldomain_mod($sldomain_mod);		//分站URL显示模式
		$url->set_subsite_mod($subsite_mod);		//分站URL显示模式
		$address = $url->create($url_exp, $domain);	//开始生成

		//要求完整路径时
		if($full_url && 'http://' != strtolower(substr($address, 0, 7))) {
			if($subsite_mod == MF_URL_CITY_SUBDOMAIN) {
				if($domain_without[$dw_key] || !G('city.domain')) {
					$address = S('siteurl').ltrim($address,'/');
				} else {
					$address = 'http://'.G('city.domain').'.'.ms_domain::get_parent(S('siteurl')).URLROOT.'/'.ltrim($address,'/');
				}
			} else {
				$address = 'http://'.ms_domain::get_domain(S('siteurl'),false,'http',false).'/'.ltrim($address,'/');
			}
			//$address = S('siteurl') . ltrim($address,'/');
			/*
			if(_G('sldomain')) {
				//二级域名被模块拦截成功，则说明当前域名不是网站常规域名
				if($this->subsite_mod == MF_URL_CITY_SUBDOMAIN && _G('city','domain')) {
					$fldomain = get_fl_domain(S('siteurl'));
					$address = 'http://'._G('city','domain').'.'.$fldomain.'/'.ltrim($address,'/');
				} else {
					$address = S('siteurl').ltrim($address,'/');
				}
			} else {
				if($domain_without[$dw_key]) {
					$address = S('siteurl').ltrim($address,'/');
				} else {
					$address = 'http://'.get_current_domain().'/'.ltrim($address,'/');
				}
			}
			*/
		}
		return $address;
	}

	/**
	 * 解析当前打开的页面URL
	 * @return [type] [description]
	 */
	private function _pares_url()
	{
		//URL获取
		$mod = $uri = null;
		if(isset($_GET['Rewrite'])) {
			//HTML伪静态
			$mod = MF_URL_REWRITE_HTML;
			$uri = $_GET['Rewrite'];
		} elseif($_GET['Pathinfo']) {
			//Pathinfo目录形式
			$mod = MF_URL_REWRITE_PATH;
			$uri = $_GET['Pathinfo'];
		} elseif(strpos(request_uri(), '/index.php/') !== false) {
			//!$this->hide_index
			//通过入口文件引导的模拟rewrite功能
			list($mod, $uri) = $this->_parse_index_url();
			//当前来路并没有隐藏index.php
			$this->real_hide_index = false;
		} elseif(!$_SERVER['QUERY_STRING']) {
			$mod = $this->rewrite_mod;
			$uri = '';
		} else {
			//默认模式
			$mod = MF_URL_REWRITE_NORMAL;
			$uri = request_uri();
		}
		//当前URL模式和后台设置的不一致时，后台未打开其他模式识别功能时，提示页面不存在(原始URL除外)
		if($mod != MF_URL_REWRITE_NORMAL && $this->rewrite_mod != $mod && !$this->rewrite_location) {
			$this->url_not_found = true;
		} else {
			$this->url = $this->_factory($mod);
			$this->url_exp = $this->url->pares($uri); 		//解析URL，转换成URL表达式
			$this->url_not_found = $this->url->not_found; 	//URL是否无效，无效则说明页面不存在
		}
		if(!$this->url_not_found) {
			$this->_pares_urlexp(); 	//解析url表达式成为可用url参数数组
			$this->_import_get(); 		//导入参数到$_GET
		}
		$this->real_rewrite_mod = $mod;
		$this->use_rewrite = $mod != MF_URL_REWRITE_NORMAL;
	}

	/**
	 * URL重写使用了入口文件做引导的
	 * @return array [description]
	 */
	private function _parse_index_url()
	{
		$result = array('', '');
		// 获取请求的URI
		foreach (array('REQUEST_URI', 'HTTP_X_REWRITE_URL') as $var) {
			if ($uri = $_SERVER[$var]) {
				$uri = trim($uri);
				break;
			}
		}
		if(!$uri) return $result;
		// 去除多个/的情况
		$uri = preg_replace('%/{2,}%i', '/', $uri);
		$i = strpos(strtolower($uri), URLROOT . '/index.php/');
		if ($i !== false) {
			$uri = substr($uri, $i + strlen(URLROOT . '/index.php/'));
		} else {
			return $result;
		}
		//使用系统设置的URL重写模式
		$this->use_sim = true; //模拟URL重写
		$result[0] = $this->rewrite_mod;
		$result[1] = $uri;
		return $result;
		/*
		//通过是否有“/”，来判断是否是PATHINFO模式
		if(strpos($uri, '/') === false) {
			//通过文件后缀判断模拟rewrite模式
			$ext = substr(strtolower($uri), -5);
			if('.html' == $ext) {
				//模拟HTML伪静态
				$result[0] = MF_URL_REWRITE_HTML;
				$result[1] = $uri;
			} else {
				//不是系统的URL，系统页面不存在
				$this->url_not_found = true;
			}
		} else {
			//初步判断PATHINFO模式，进入相关类后再进行判断
			$result[0] = MF_URL_REWRITE_PATH;
			$result[1] = $uri;
		}
		return $result; //返回array(模式,URL)
		*/
	}

	/**
	 * 判断一些模块和模块行为不需要城市属性（分站目录或者子域名）
	 * @param  string $module 模块标识
	 * @param  string $act    模块内行为
	 * @return bool         
	 */
	private function _domain_without($module, $act)
	{
		static $citypath_without = null;
		if($module=='index'&&!$act) return false;
		if(is_null($citypath_without)) {
			$cw_arr = array();
			$cfg_cw = S('citypath_without');
			is_string($cfg_cw) && $cw_arr = explode("\r\n", $cfg_cw);
			foreach(_G('modules') as $md) {
				//所有模块的会员管理行为
				$cw_arr[] = "$md[flag]/member";
			}
			$cw_arr[] = 'member/*';
			$cw_arr[] = 'index/*';
			foreach ($cw_arr as $key => $match) {
				list($m, $a) = explode('/', $match);
				$citypath_without[$m][] = $a;
			}
			if(!$citypath_without) $citypath_without = false;
		}
		if($citypath_without[$module]) foreach($citypath_without[$module] as $a) {
			if($a == '*' || $a == $act) return true;
			//数字
			if($a == '{num}' && is_numeric($act)) return true;
		}
		if($citypath_without['*']) foreach($citypath_without['*'] as $a) {
			if($act == $a) return true;
		}
		return false;
	}

	/**
	 * 实例化url重写类
	 * @param  [type] $rewrite_mod 重写模式类型
	 * @return ms_url_base
	 */
	private function _factory($rewrite_mod)
	{
		$classname = 'ms_url_' . $rewrite_mod;
		return new $classname();
	}

	/**
	 * 把url表达式解析成程序可用有序数组
	 * @return array
	 */
	private function _pares_urlexp()
	{
		if(!$this->url_exp) return;
		$this->url_exp = trim($this->url_exp, '/');
		$this->params = explode('/', $this->url_exp); //参数数组
		//根据规则生成URL正式参数数组
		$this->get['m'] = $this->params[0]; //模块名
		if(isset($this->params[1])) {
			$this->get['act'] = $this->params[1];  //行为
		}
		if(count($this->params) > 2) {
			//参数配对
			for ($i = 2; $i < count($this->params) ; $i=$i + 2) {
				$key = $this->params[$i];
				$value = $this->params[$i+1];
				//使用系模拟URL重写，需要手动 rawurldecode
				if($this->use_sim) {
					$key = rawurldecode($key);
					$value = rawurldecode($value);
				}
				//中文兼容需要再次 rawurldecode
				if($this->chinses_compatible && $this->real_rewrite_mod != MF_URL_REWRITE_NORMAL) {
					$key = rawurldecode($key);
					$value = rawurldecode($value);
				}
				$this->get[$key] = str_replace('_f_','-',$value);
			}
		}
	}

	/**
	 * url参数数字导入到$_GET中，供系统在GET种获取使用
	 */
	private function _import_get()
	{
		if(!$this->get) return;
		foreach ($this->get as $key => $value) {
			$_GET[$key] = $value;
		}
	}
}

/**
 * URL重写抽象基类
 */
abstract class ms_url_base
{
	/**
	 * 原始URI，需要解析成系统可识别的URL表达式
	 * @var string
	 */
	public $uri = '';
	/**
	 * URL解析后获取的模块标识
	 * @var string
	 */
	public $module = '';
	/**
	 * URL解析后获取的模块行为
	 * @var string
	 */
	public $act = '';
	/**
	 * URL解析后获取的分站名
	 * @var string
	 */
	public $subsite = '';
	/**
	 * 子域名
	 * @var string
	 */
	public $subdomain = '';
	/**
	 * 是否不存在的url
	 * @var boolean
	 */
	public $not_found = false;
	/**
	 * 当前访问的域名和系统设置的不相同
	 * @var boolean
	 */
	private $diff_domain = false;
	/**
	 * 分站URL形式
	 * @var string
	 */
	public $sldomain_mod = MF_URL_SLDOMAIN_NONE;
	/**
	 * 分站URL形式
	 * @var string
	 */
	public $subsite_mod = MF_URL_CITY_PATHNAME;
	/**
	 * url自定义替换类
	 * @var [type]
	 */
	protected $pregurl = null;
	/**
	 * 是否隐藏唯一入口PHP文件
	 * @var boolean
	 */
	protected $hide_index = true;
	/**
	 * 唯一入口index的网页文件名
	 * @var string
	 */
	protected $index_name = 'index.php';
	/**
	 * 中文参数兼容
	 * @var boolean
	 */
	private $chinses_compatible = false;

	public function __construct()
	{
		$this->chinses_compatible = S('rewritecompatible') ? true : false;
		$this->_pregurl_factory();
	}

	/**
	 * 设置当前分站URL模式
	 * @param string $mod 分站URL模式
	 */
	public function set_sldomain_mod($mod)
	{
		$this->sldomain_mod = $mod;
	}

	/**
	 * 设置当前分站URL模式
	 * @param string $mod 分站URL模式
	 */
	public function set_subsite_mod($mod)
	{
		$this->subsite_mod = $mod;
	}

	/**
	 * 设置hide_index
	 * @param bool $boolean 是否隐藏index
	 */
	public function set_hide_index($boolean)
	{
		$this->hide_index = (bool) $boolean;
	}

	/**
	 * URL参数编码,$rawurlencode中文兼容。留空表示按配置，false表示不使用，true表示强制使用
	 * @param  string $str 准备被编码的URL参数
	 * @return string
	 */
	public function urlencode($str)
	{
		if(is_numeric($str)) return $str;
		$str = rawurlencode($str);
		if( $this->chinses_compatible ) {
			$str = rawurlencode($str);
		}
		return $str;
	}

	/**
	 * 针对url表达式生成相应的url地址
	 * @param  [type] $url_exp [description]
	 * @param  string $domain  [description]
	 * @return [type]          [description]
	 */
	public function create($url_exp, $domain='') {}

	/**
	 * 针对url表达式生成相应的url地址
	 * @param  array  $url_exp_arr [description]
	 * @param  boolean $include_subsite_url URL地址是否包含分站信息
	 * @param  boolean $full_url    URL地址是否显示完整路径（如果相关配置必须要显示完整路径，则此参数被忽略）
	 * @return string
	 */
	public function create2($url_exp_arr, $include_subsite_url = TRUE, $full_url = TRUE) {}

	/**
	 * 传入的url地址进行解析成url表达式
	 * @param  [type] $url [description]
	 * @return [type]      [description]
	 */
	public function pares($uri) 
	{
		//当前准备解析的URL
		$this->uri = $uri;
		//获取URL里的子域名
		$this->subdomain = $this->_get_subdomain();
		if($this->subdomain) {
			//解析子域名，是否是存在多个级别(二级域名 shopname.域名，三级域名：shopname.shop.域名)
			if(strpos($this->subdomain, '.') !== false) {
				//三级域名暂时不支持，这里设置为页面不存在
				$this->not_found = true;
			}
			//检测是否是模块或者分站名
			//
			//暂时不支持模块子域名形式URL
			// if(check_module($this->subdomain)) {
			// 	//是一个模块子域名
			// 	$this->module = $this->subdomain;
			// }
			if(ms_subsite::check_exists($this->subdomain)) {
				//是一个分站子域名
				$this->subsite = $this->subdomain;
			} else {
				//未知的子域名，让后续的模块HOOK子域名
				$this->unknow_subdomain = $this->subdomain;
			}
		}
		return !$this->not_found;
	}

	/**
	 * 解析当前url地址的子域名
	 */
	private function _get_subdomain() 
	{
		$siteurl = S('siteurl');
		//如果URL和后台设置的不一致，进入获取子域名获取
		if(!ms_domain::compare_with($siteurl)) {
			//判断一级域名是否一致
			if(ms_domain::get_top() != ms_domain::get_top($siteurl)) {
				//一级域名不一致，说明当前域名和后台设置的域名不同，可能是多域名绑定，跳转URL解析
				$this->diff_domain = true;
				$this->not_found = true;
				return;
			}
			//获取子域名
			if(ms_domain::get_prefix($siteurl)) {
				//当前系统安装在子域名下，则从子域名下着下一级的子域名
				$ext = ms_domain::get_domain($siteurl);
			} else {
				$ext = ms_domain::get_parent($siteurl);
			}
			//子域名
			return substr(ms_domain::get_domain(), 0, -(strlen($ext) + 1));
		}
		return;
	}

	//获取一个用于自定义URL规则文件改写的类
	private function _pregurl_factory() {
		$classname = get_class($this);
		$rewrite_mod = str_replace('ms_url_', '', $classname);
		if($rewrite_mod != MF_URL_REWRITE_NORMAL) {
			$classname = 'ms_pregurl_' . $rewrite_mod;
			$this->pregurl = new $classname();
		}
	}
}

/**
 * 原始动态URL处理类
 */
class ms_url_normal extends ms_url_base 
{
	/**
	 * 使用唯一入口,原始url采用入口(index.php?......)
	 * @var boolean
	 */
	private $single_index = false;	//

	public function __construct() 
	{
		parent::__construct();
		//单一入口
		$this->single_index = S('single_index') ? true : false;
	}

	public function create($url_exp, $domain = '') 
	{
		$url = '';
		if($url_exp) {
			$exp = explode('/', $url_exp);
			if(count($exp)>0) {
				$url = $this->single_index ? ($this->index_name.'?m=' . $exp[0]) : ($exp[0].'.php');
				if(isset($exp[1])) $url .=  ($this->single_index?'&':'?') . 'act=' . $exp[1];
				if(count($exp) > 2) for ($i=2; $i < count($exp); $i=$i+2) {
					$url .= '&'.rawurlencode($exp[$i]).'='.rawurlencode($exp[$i+1]);
				}
			}
		}

		if(!$domain) return URLROOT . '/' . $url;
		if($this->subsite_mod == MF_URL_CITY_SUBDOMAIN) {
			return 'http://' . $domain . '.'. ms_domain::get_parent(S('siteurl')) . URLROOT . '/' . $url;
		} else {
			return URLROOT . '/' . $url;
		}
	}

	public function create2($url_exp_arr, $include_subsite_url = TRUE, $full_url = TRUE) 
	{
		$url = '';
		$domain = ms_domain::get_parent(S('siteurl'));
		$split = '?';
		if($this->sldomain_mod == MF_URL_SLDOMAIN_MODULE) {
			//二级域名是模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url = "http://{$url_exp_arr['module']}.{$domain}".URLROOT.'/';
			} else {
				$url = S('siteurl');
			}
			//处理城市分站,暂无支持原始动态连接包含分站参数
			//$url .= '?city='.$url_exp_arr['city'];
		} else {
			if($this->sldomain_mod == MF_URL_SLDOMAIN_CITYSITE) {
				//二级域名是分站城市，同时URL需要包含分站信息时
				if($url_exp_arr['city']&&$include_subsite_url) {
					$url = "http://{$url_exp_arr['city']}.{$domain}".URLROOT.'/';
				} else {
					$url = S('siteurl');
				}
			} else {
				$url = $full_url?S('siteurl'):(URLROOT.'/');
			}
			//处理模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url .= $this->single_index?($this->index_name.'?m='.$url_exp_arr['module']):($url_exp_arr['module'].'.php');
				$this->single_index && $split = '&';
			} else {
				$url .= $this->single_index?$this->index_name:'';
			}
		}
		if($url_exp_arr['act']) {
			$url .= $split.'act='.$url_exp_arr['act'];
			$split = '&';
		}
		if($url_exp_arr['params']) foreach ($url_exp_arr['params'] as $key => $value) {
			$url .= $split.rawurlencode($key).'='.rawurlencode($value);
		}
		return $url;
	}

	public function pares($uri) 
	{
		if(!parent::pares($uri)) return;
		if(defined('IN_ADMIN')) return;

		$url_arr = parse_url($uri);
		$this->module = $url_arr['path'] ? basename($url_arr['path'],'.php') : 'index';
		!$this->module and $this->module = 'index';
		if(strtolower(dirname($url_arr['path'])) == strtolower(URLROOT_PRE)) {
			$this->module = 'index';
		} else {
			$this->module 	= $_GET['m']?:$this->module;
		}

		$this->module 	= $_GET['m']?:$this->module;
		$this->act 		= $this->act?:($_GET['act']?:'');

		if(!check_module($this->module)) {
			$this->not_found = true;
			return false;
		}

		if(!$url_arr['query']||!$this->act) return $this->module.'/'.$this->act;
		parse_str($url_arr['query'], $params);
		unset($params['m'], $params['act']);

		$url_exp = '';
		if($params) {
			$url_exp = $this->module.'/'.$this->act;
			foreach ($params as $key => $value) {
				$url_exp .= '/'.$key.'/'.$value;
			}
		}

		return $url_exp;
	}
}

/**
 * html伪静态URL类
 */
class ms_url_html extends ms_url_base 
{
	/**
	 * html伪静态url后缀
	 * @var string
	 */
	private $html_ext = '.html';

	public function create($url_exp, $domain = '') 
	{
		$url_exp = str_replace('-', '_f_', $url_exp);
		$exp = explode('/', $url_exp);
		$url = '';
		foreach ($exp as $key => $value) {
			if(is_string($value)) $value = $this->urlencode($value);
			$url .= '-' . $value;
		}
		$url = ltrim($url, '-');
		if($url) $url .= $this->html_ext;
		//URL自定义替换
		if($this->pregurl && $url) {
			$url = $this->pregurl->preg($url, MF_URL_PREG_CREATE);
		}
		//入口文件(index.php/)
		$index_name = !$this->hide_index ? ($this->index_name . ($url ? '/' : '')) : '';
		//返回URL
		if(!$domain) return URLROOT . '/' . $index_name . $url;
		if($this->subsite_mod == MF_URL_CITY_SUBDOMAIN) {
			return 'http://' . $domain . '.'. ms_domain::get_parent(S('siteurl')) . URLROOT . '/' . $index_name . $url;
		} else {
			return URLROOT . '/' . $index_name . $url;
		}
	}

	public function create2($url_exp_arr, $include_subsite_url = TRUE, $full_url = TRUE) 
	{
		if($url_exp_arr) foreach ($url_exp_arr as $key => $value) {
			$url_exp_arr[$key] = str_replace('-', '_f_', $value);
		}
		$url = '';
		$domain = ms_domain::get_parent(S('siteurl'));
		$pre = !$this->hide_index?($this->index_name.'/'):'';
		if($this->sldomain_mod == MF_URL_SLDOMAIN_MODULE) {
			//二级域名是模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url = "http://{$url_exp_arr['module']}.{$domain}".URLROOT.'/';
			} else {
				$url = S('siteurl');
			}
			//处理城市分站
			/*
			if($url_exp_arr['city']&&$include_subsite_url){
				if($this->subsite_mod == MF_URL_CITY_NORMAL) {
					$url .= $pre.$url_exp_arr['city'].'-';
					$pre = '';
				}
			}
			*/
		} elseif($this->sldomain_mod == MF_URL_SLDOMAIN_CITYSITE) {
			//二级域名是分站城市，同时URL需要包含分站信息时
			if($url_exp_arr['city']&&$include_subsite_url) {
				$url = "http://{$url_exp_arr['city']}.{$domain}".URLROOT.'/';
			} else {
				$url = S('siteurl');
			}
			//处理模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url .= $pre.$url_exp_arr['module'].'-';
				$pre = '';
			} else {
				//$url .=  $this->hide_index?($this->index_name.'/')
			}
		} else {
			$url = $full_url?S('siteurl'):(URLROOT.'/');
			//处理城市分站
			// if($url_exp_arr['city']&&$include_subsite_url){
			// 	if($this->subsite_mod == MF_URL_CITY_NORMAL) {
			// 		$url .= $pre.$url_exp_arr['city'].'-';
			// 		$pre = '';
			// 	}
			// }
			//处理模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url .= $pre.$url_exp_arr['module'].'-';
				$pre = '';
			}
		}
		if(!$url_exp_arr['act']) return $url;

		if($url_exp_arr['act']) {
			$url .= $pre.$url_exp_arr['act'].'-';
		}
		if($url_exp_arr['params']) foreach ($url_exp_arr['params'] as $key => $value) {
			$url .= $this->urlencode($key).'-'.$this->urlencode($value).'-';
		}
		$url = trim($url, '-');
		if($url) $url .= $this->html_ext;
		//URL自定义替换
		if($this->pregurl && $url) {
			$url = $this->pregurl->preg($url, MF_URL_PREG_CREATE);
		}

		return $url;
	}

	public function pares($uri)
	{
		if(!parent::pares($uri)) return;
		if(!$uri) return '';

		$uri = trim(str_replace('//', '/', $uri),'/');
		//URL自定义替换
		if($this->pregurl && $uri) {
			$uri = $this->pregurl->preg($uri, MF_URL_PREG_PARSE);
		}

		$url_exp = trim($uri);
		if(substr($url_exp, -5) == $this->html_ext) {
			$url_exp = substr($url_exp, 0, -5);
		}
		$params = explode('-', $url_exp);
		$params[0] = strtolower($params[0]);
		if($params[0] == 'index' || check_module($params[0])) {
			$this->module = $params[0];
			$this->act 	  = $params[1] ?: '';
			return str_replace('-', '/', $url_exp);
		}
		$this->not_found = true;
		return false;
	}
}

/**
 * pathinfo目录形式URL类
 */
class ms_url_pathinfo extends ms_url_base 
{
	//目录层级
	private $dir_level = 1;
	//目录后缀
	private $ext = '';

	public function __construct() 
	{
		parent::__construct();
		$this->ext = strtolower(trim(S('rewrite_mod_pathinfo_ext')));
	}

	/**
	 * 生成目录形式URL
	 * @param  string $url_exp URL表达式
	 * @param  string $domain  分站名称
	 * @return string
	 */
	public function create($url_exp, $domain = '') 
	{
		$url_exp = str_replace('-', '_f_', $url_exp); // 单横杠与UEL会冲突，则例用_f_进行占位表示
		$exp = explode('/', $url_exp);
		$url = '';
		foreach ($exp as $key => $value) {
			if(is_string($value)) $value = $this->urlencode($value);
			$url .= ($key > $this->dir_level ? '-' : '/') . $value; //目录级别超过时，用-进行连接
		}
		$url = trim($url, '/');
		//URL自定义替换
		if($this->pregurl && $url) {
			$url = $this->pregurl->preg($url, MF_URL_PREG_CREATE);
		}
		//加后缀
		if($this->ext && $url && substr($url, -1) != '/') {
			$url .= '.' . $this->ext;
		}
		//入口文件(index.php，如果url为空，则结尾不加/)
		$index_name = !$this->hide_index ? ($this->index_name . ($url ? '/' : '')) : '';
		//返回生成网址
		if(!$domain) {
			return URLROOT . '/' . $index_name . $url;
		} else if ($this->subsite_mod == MF_URL_CITY_PATHNAME) {
			return URLROOT . '/' . $index_name . $domain . '/' . $url;
		} elseif ($this->subsite_mod == MF_URL_CITY_SUBDOMAIN) {
			return 'http://' . $domain . '.'. ms_domain::get_parent(S('siteurl')) . URLROOT . '/' . $index_name . $url;
		} else {
			return URLROOT . '/' . $index_name . $url;
		}
	}

	public function create2($url_exp_arr, $include_subsite_url = TRUE, $full_url = TRUE) 
	{
		if($url_exp_arr) foreach ($url_exp_arr as $key => $value) {
			$url_exp_arr[$key] = str_replace('-', '_f_', $value);
		}
		$url = '';
		$domain = ms_domain::get_parent(S('siteurl'));
		$pre = !$this->hide_index?($this->index_name.'/'):'';
		if($this->sldomain_mod == MF_URL_SLDOMAIN_MODULE) {
			//二级域名是模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url = "http://{$url_exp_arr['module']}.{$domain}".URLROOT.'/';
			} else {
				$url = S('siteurl');
			}
			//处理城市分站
			if($url_exp_arr['city']){
				if($this->subsite_mod == MF_URL_CITY_PATHNAME) {
					$url .= $pre.$url_exp_arr['city'].'/';
					$pre = '';
				}
			}
		} elseif($this->sldomain_mod == MF_URL_SLDOMAIN_CITYSITE) {
			//二级域名是分站城市，同时URL需要包含分站信息时
			if($url_exp_arr['city']&&$include_subsite_url) {
				$url = "http://{$url_exp_arr['city']}.{$domain}".URLROOT.'/';
			} else {
				$url = S('siteurl');
			}
			//处理模块
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url .= $pre.$url_exp_arr['module'].'/';
				$pre = '';
			} else {
				//$url .=  $this->hide_index?($this->index_name.'/')
			}
		} else {
			$url = $full_url?S('siteurl'):(URLROOT.'/');
			//分组目录
			if($this->subsite_mod == MF_URL_CITY_PATHNAME && $url_exp_arr['city'] && $include_subsite_url) {
				$url .= $pre.$url_exp_arr['city'].'/';
				$pre = '';
			}
			//模块目录
			if($url_exp_arr['module']&&!in_array($url_exp_arr['module'], array('index','mooer'))) {
				$url .= $pre.$url_exp_arr['module'].'/';
				$pre = '';
			}
		}

		if($url_exp_arr['act']) {
			$url .= $pre.$url_exp_arr['act']; //此处后面不加"/"，以为是最终页面，加后缀时不能有"/"
			if($url_exp_arr['params']) foreach ($url_exp_arr['params'] as $key => $value) {
				$url .= '/'.$this->urlencode($key).'/'.$this->urlencode($value);
			}
		}


		//URL自定义替换
		if($this->pregurl && $url) {
			$url = $this->pregurl->preg($url, MF_URL_PREG_CREATE);
		}
		//加后缀
		if($this->ext && $url && substr($url, -1) != '/') {
			$url .= '.' . $this->ext;
		}

		return $url;
	}

	/**
	 * 解析URL网址
	 * @param  string $url 准备解析的目录形式的网址
	 * @return string      解析后系统认知的URL表达式
	 */
	public function pares($uri) 
	{
		if(!parent::pares($uri)) return;
		if(!$uri) return '';

		$ext = strtolower(pathinfo($uri, PATHINFO_EXTENSION));
		if(strlen($this->ext) > 0 && $ext == $this->ext) {
			$uri = substr($uri, 0, -(strlen($ext) + 1));
		}
		$uri = trim(str_replace('//', '/', $uri), '/');
		list($p1, $p2) = explode('/', $uri);
		$p1 = strtolower($p1);
		//判断url首个参数是否是分站名
		if($p1 != 'index' && !check_module($p1)) {
			//分站标识
			if(!ms_subsite::check_exists($p1)) {
				//如果不是分站表示，则自定义规则，进入自定义URL解析判断
				if($this->pregurl && $uri) {
					$tmp_uri = $this->pregurl->preg($uri, MF_URL_PREG_PARSE);
					
					$used_preg = true;
					if($tmp_uri == $uri) {
						//自定义规则不起作用
						$this->not_found = true;
						return false;
					} else {
						$uri = $tmp_uri;
					}
				} else {
					$this->not_found = true;
					return false;
				}
			} else {
				$this->subsite = $p1;
				//从url地址中删除这个分站标识
				$uri = ltrim(substr($uri, strlen($p1)), '/');				
			}
		}
		//自定义规则URL替换系统默认的URL地址
		if(!$used_preg && $this->pregurl && $uri) {
			$uri = $this->pregurl->preg($uri, MF_URL_PREG_PARSE);
		}
		$url_exp = str_replace('-', '/', $uri);
		$params = explode('/', $url_exp);
		$params[0] = strtolower($params[0]);
		if(!$params[0] || $params[0] == 'index' || check_module($params[0])) {
			$this->module = $params[0] ?: 'index';
			$this->act 	  = $params[1] ?: '';
			return $url_exp;
		}

		$this->not_found = true;
		return false;
	}
}

/**
 * 自定义URL替换类
 */
abstract class ms_pregurl {
	/**
	 * 规则列表
	 * @var array
	 */
	protected $preg = array();
	/**
	 * 加载配置文件
	 * @param string $rewrite_mod URL重写模式
	 */
	public function __construct($rewrite_mod) {
		$this->load_config($rewrite_mod);
	}

	/**
	 * 载入规则文件，并解析规则文件
	 * @param  string $rewrite_mod URL重写模式
	 */
	protected function load_config($rewrite_mod) {
		$filename = MUDDER_ROOT . 'data' . DS . 'rewrite_' . $rewrite_mod . '.inc';
		is_file($filename) and $this->pares_config(@file_get_contents($filename));
	}

	/**
	 * 解析规则文件
	 * @param  string $content 准备解析的规则内容
	 */
	private function pares_config($content) {
		$content = explode("\n", preg_replace("/\s*(\r\n|\n\r|\n|\r)\s*/", "\n", $content));
		foreach($content as $v) {
			if(trim($v)=='') continue;
			$v = trim(preg_replace("/\s+/",' ', str_replace("\t", " ", $v)));
			if($v{0}=='#') continue;
			list($preg, $replace, $params) = explode(' ', $v);
			$this->preg[$preg] = array(
				'key' => md5($preg),
				'replace'=>$replace,
			);
			if($params) {
				$this->preg[$preg]['params'] = $this->_pares_config_params($params);
			} else {
				$this->preg[$preg]['params'] = array();
			}
		}
	}

	/**
	 * 解析单条规则内的替换规则参数
	 * @param  string $params 参数聚合，需要解析后才能被类所使用
	 * @return array         解析后的规则参数，数组形式
	 */
	private function _pares_config_params($params) {
		$result = array();
		if(!$params) return $result;
		if(substr($params, 0, 1)=='[' && substr($params, -1)==']') {
			$params = substr(strtoupper($params), 1, -1);
		}
		return explode(',', $params);
	}

	/**
	 * 检测替换url
	 * @param  string $url       准备替换的URL地址
	 * @param  string $preg_type URL表达式行为（生成URL时，解析URL时）
	 * @return string            替换后的url地址
	 */
	public function preg($url, $preg_type) {
		//频繁调用，处理为静态变量
		static $preg_objects = array();
		if(!$this->preg) return $url;
		foreach($this->preg as $preg => $match) {
			$cls = $preg_objects[$match['key']];
			if(!$cls) {
				$cls = $this->preg_object($match);
				$preg_objects[$match['key']] = $cls;
			}
			if(!$cls->params[$preg_type]) continue;
			//B标记表示匹配就跳出
			if($cls->params['B'] && preg_match("/^$preg$/", $url)) {
				return $url;
			}
			//！！！此处正则需要优化（按模块分组获取正则，避免全部正则放在一起）
			$newurl = preg_replace("/^$preg$/", $match['replace'], $url);
			if($newurl != $url) {
				//L表示终止后面的正则表达
				if($cls->params['L']) return $newurl;
				$url = $newurl;
			}
		}
		return $url;
	}

	private function preg_object($match) {
		$class = new stdClass();
		if(is_array($match['params'])) foreach ($match['params'] as $value) {
			$class->params[$value] = TRUE;
		}
		return $class;
	}
}

/**
* pathinfo模式下自定义URL
*/
class ms_pregurl_pathinfo extends ms_pregurl {
	public function __construct() {
		parent::__construct(MF_URL_REWRITE_PATH);
	}
}

/**
* pathinfo模式下自定义URL
*/
class ms_pregurl_html extends ms_pregurl {
	public function __construct() {
		parent::__construct(MF_URL_REWRITE_HTML);
	}
}

/* end */